#include "SegmentMotionHandlerDialog.h"
#include "ui_SegmentMotionHandlerDialog.h"

#include <MMM/FactoryPluginLoader.h>
#include <QFileDialog>
#include <QSignalMapper>
#include <QLabel>

#include "SegmentationPointsWidget.h"

SegmentMotionHandlerDialog::SegmentMotionHandlerDialog(QWidget* parent) :
    QDialog(parent),
    ui(new Ui::SegmentMotionHandlerDialog),
    currentSegmentationWidget(nullptr),
    currentSegmentationPointsWidget(nullptr)
{
    ui->setupUi(this);

    ui->SegmentationResultGroupBox->setEnabled(false);

    connect(ui->mmmRadioButton, SIGNAL(toggled(bool)),this, SLOT(setMMMSegmentMotionHandlerType(bool)));
    connect(ui->mmmMarkerRadioButton, SIGNAL(toggled(bool)),this, SLOT(setMMMMarkerSegmentMotionHandlerType(bool)));
    connect(ui->c3dRadioButton, SIGNAL(toggled(bool)),this, SLOT(setC3DMarkerSegmentMotionHandlerType(bool)));
    connect(ui->chooseMotionBox, SIGNAL(currentIndexChanged(int)), this, SLOT(setCurrentMotion(int)));
    connect(ui->chooseSegmenterBox, SIGNAL(currentIndexChanged(QString)), this, SLOT(setSegmenter(QString)));
    ui->segmentButton->setEnabled(false);
    connect(ui->playButton, SIGNAL(clicked()), this, SLOT(playSegmentMotionHandler()));
    connect(ui->segmentButton, SIGNAL(clicked()), this, SLOT(segmentMotion()));
    connect(ui->cancelButton, SIGNAL(clicked()), this, SLOT(close()));
    connect(ui->listWidget, SIGNAL(itemClicked(QListWidgetItem*)), this, SLOT(close()));

    playSegmentMotionHandlerTimer = new SoTimerSensor(playSegmentMotionHandlerTimerCallback, this);
    playSegmentMotionHandlerTimer->setInterval(SbTime(1.0f));
    playSegmentMotionHandlerTimer->setBaseTime(SbTime::getTimeOfDay());
}

SegmentMotionHandlerDialog::~SegmentMotionHandlerDialog()
{
    delete ui;
}

void SegmentMotionHandlerDialog::segmentMotion(MMM::MotionList motions) {
    loadMotions(motions);
    this->show();
}

void SegmentMotionHandlerDialog::loadMotions(MMM::MotionList motions) {
    ui->chooseMotionBox->clear();
    currentMotion = nullptr;
    this->motions = motions;

    int i = 0;
    for (MMM::MotionPtr motion : motions) {
        ui->chooseMotionBox->addItem(QString::fromStdString(motion->getName()));
        if (motion->getName() == motionName) ui->chooseMotionBox->setCurrentIndex(i);
        i++;
    }
    if (!currentMotion) setCurrentMotion(0);

    if (!currentSegmentationPointsWidget) createSegmentationPointsWidget();
}

void SegmentMotionHandlerDialog::setCurrentMotion(int index) {
    if (index >= 0 && motions.size() > static_cast<std::size_t>(index)) {
        currentMotion = motions[index];
        if (segmenter) segmenter->setMotion(currentMotion);
        if (!currentMotion->getSensorByType("MoCapMarker")) {
            ui->c3dRadioButton->setEnabled(false);
        } else {
            ui->c3dRadioButton->setEnabled(true);
            ui->c3dRadioButton->setChecked(true);
            segType = MMM::SegmentationType::C3D_MARKER;
        }
        if (!currentMotion->getSensorByType("ModelPose")) {
            ui->mmmRadioButton->setEnabled(false);
            ui->mmmMarkerRadioButton->setEnabled(false);
        } else {
            ui->mmmRadioButton->setEnabled(true);
            ui->mmmMarkerRadioButton->setEnabled(true);
            ui->mmmRadioButton->setChecked(true);
            segType = MMM::SegmentationType::MMM;
        }
        motionName = currentMotion->getName();
        enableSegmentMotion();
    }
}

void SegmentMotionHandlerDialog::update(std::map<std::string, MMM::MotionSegmenterFactoryPtr > motionSegmenterFactories) {
    this->motionSegmenterFactories = motionSegmenterFactories;

    ui->chooseSegmenterBox->clear();
    for (const auto &segmenter : motionSegmenterFactories) ui->chooseSegmenterBox->addItem(QString::fromStdString(segmenter.first));
    if (motionSegmenterFactories.size() == 0) setSegmenter(QString());
}

void SegmentMotionHandlerDialog::setSegmenter(QString name) {
    if (motionSegmenterFactories.find(name.toStdString()) != motionSegmenterFactories.end())
        segmenter = motionSegmenterFactories[name.toStdString()]->createMotionSegmenter(currentMotion, segType);
    else segmenter = nullptr;

    enableSegmentMotion();

    QLayoutItem* item;
    while ((item = ui->SegmentationLayout->takeAt(0)) != nullptr) {
        delete item->widget();
        delete item;
    }

    if (segmenter) {
        currentSegmentationWidget = segmenter->getWidget();
        ui->SegmentationLayout->addWidget(currentSegmentationWidget);
    }
    else {
        ui->SegmentationLayout->addWidget(new QLabel(tr("Segmentation plugin missing!")));
        currentSegmentationWidget = nullptr;
    }
}
void SegmentMotionHandlerDialog::enableSegmentMotion() {
    if (currentMotion && segmenter) ui->segmentButton->setEnabled(true);
    else ui->segmentButton->setEnabled(false);
}

void SegmentMotionHandlerDialog::setMMMSegmentMotionHandlerType(bool value) {
    if (value) setSegmentMotionHandlerType(MMM::SegmentationType::MMM);
}

void SegmentMotionHandlerDialog::setMMMMarkerSegmentMotionHandlerType(bool value) {
    if (value) setSegmentMotionHandlerType(MMM::SegmentationType::MMM_MARKER);
}

void SegmentMotionHandlerDialog::setC3DMarkerSegmentMotionHandlerType(bool value) {
    if (value) setSegmentMotionHandlerType(MMM::SegmentationType::C3D_MARKER);
}

void SegmentMotionHandlerDialog::setSegmentMotionHandlerType(MMM::SegmentationType type) {
    segType = type;
    if (segmenter) segmenter->setSegmentationType(type);
}

void SegmentMotionHandlerDialog::segmentMotion() {
    stopPlaySegmentMotionHandler();
    segmentedTimesteps = segmenter->segment();

    if (segmentedTimesteps.size() > 0) ui->SegmentationResultGroupBox->setEnabled(true);
    else ui->SegmentationResultGroupBox->setEnabled(false);

    if (!currentSegmentationPointsWidget) createSegmentationPointsWidget();
    else currentSegmentationPointsWidget->repaint(segmentedTimesteps, currentMotion->getMinTimestep(), currentMotion->getMaxTimestep());

    ui->listWidget->clear();


    signalMapper = new QSignalMapper(this);
    unsigned int i = 1;
    for (MMM::SegmentationPtr seg : segmentedTimesteps) {
        std::stringstream name;
        name << "Segment " << i << " - Timestep " << seg->getKeyTimestep();

        QListWidgetItem* item = new QListWidgetItem(ui->listWidget);
        QRadioButton* radioButton = new QRadioButton(QString::fromStdString(name.str()));
        ui->listWidget->setItemWidget(item, radioButton);
        connect(radioButton, SIGNAL(clicked(bool)), signalMapper, SLOT(map()));
        signalMapper->setMapping(radioButton, i - 1);
        i++;
    }
    connect(signalMapper, SIGNAL(mapped(int)), this, SLOT(radioButtonClicked(int)));
}

void SegmentMotionHandlerDialog::createSegmentationPointsWidget() {
    if (currentMotion) {
        currentSegmentationPointsWidget = new SegmentationPointsWidget(segmentedTimesteps, currentMotion->getMinTimestep(), currentMotion->getMaxTimestep(), this);
        connect(currentSegmentationPointsWidget, SIGNAL(segmentPointClicked(float)), this, SIGNAL(jumpTo(float)));
        connect(currentSegmentationPointsWidget, SIGNAL(segmentPointClickedIndex(int)), this, SLOT(setTimestepChecked(int)));
        ui->SegmentationPointsLayout->addWidget(currentSegmentationPointsWidget);
    }
}

void SegmentMotionHandlerDialog::radioButtonClicked(int index) {
    float timestep = segmentedTimesteps.at(index)->getKeyTimestep();
    emit jumpTo(timestep);
    currentSegmentationPointsWidget->jumpTo(timestep);
}

void SegmentMotionHandlerDialog::setTimestepChecked(int index) {
    QListWidgetItem* item = ui->listWidget->item(index);
    ui->listWidget->scrollToItem(item);
    QRadioButton* radioButton = dynamic_cast<QRadioButton*>(ui->listWidget->itemWidget(item));
    radioButton->setChecked(true);
}

void SegmentMotionHandlerDialog::playSegmentMotionHandler() {
    if (segmentedTimesteps.size() > 0) {
        currentSegment = 0;
        if (playSegmentMotionHandlerTimer->isScheduled()) {
            playSegmentMotionHandlerTimer->unschedule();
            ui->playButton->setText(QString::fromStdString("Play"));
        } else {
            playSegmentMotionHandlerTimer->schedule();
            ui->playButton->setText(QString::fromStdString("Stop"));
        }
    }
}

void SegmentMotionHandlerDialog::playSegmentMotionHandlerTimerCallback(void* data, SoSensor* sensor) {
    SegmentMotionHandlerDialog* dialog = static_cast<SegmentMotionHandlerDialog*>(data);
    dialog->playSegmentMotionHandlerStep();
}

void SegmentMotionHandlerDialog::playSegmentMotionHandlerStep() {
    float timestep = segmentedTimesteps.at(currentSegment)->getKeyTimestep();
    emit jumpTo(timestep);
    currentSegmentationPointsWidget->jumpTo(timestep);
    setTimestepChecked(currentSegment);
    currentSegment++;
    if (currentSegment >= segmentedTimesteps.size()) stopPlaySegmentMotionHandler();
}

void SegmentMotionHandlerDialog::stopPlaySegmentMotionHandler() {
    if (playSegmentMotionHandlerTimer->isScheduled()) {
        playSegmentMotionHandlerTimer->unschedule();
        ui->playButton->setText(QString::fromStdString("Play"));
    }
}

void SegmentMotionHandlerDialog::closeEvent(QCloseEvent *event) {
    stopPlaySegmentMotionHandler();
    this->close();
}
